package org.jeecg.tender.config;


import com.google.common.collect.ImmutableMap;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.DriverTypeEnum;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeDriverService;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.firefox.GeckoDriverService;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Data
@Component
@ConfigurationProperties(prefix = "tender.web-driver")
public class WebDriverConfig {

    /** 浏览器 */
    private DriverTypeEnum type = DriverTypeEnum.CHROME;

    /** 浏览器驱动可执行路径 */
    private String path;

    /** 浏览器二进制文件路径; */
    private String binaryPath;

    /** 浏览器下载路径;下载路径需要以 / 结尾 */
    private String downloadPath;

    /** 需要的配置 */
    private List<String> options;

    /** 需要过滤的标题 */
    private List<String> filterTitles;


    @PostConstruct
    public void init() {
        // 设置驱动路径
        switch (type) {
            case CHROME:
                /** <a href="https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json">google浏览器驱动下载地址</a> */
                System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, path);
                break;
            case FIREFOX:
                System.setProperty(GeckoDriverService.GECKO_DRIVER_EXE_PROPERTY, path);
                break;
            case EDGE:
                System.setProperty(EdgeDriverService.EDGE_DRIVER_EXE_PROPERTY, path);
                break;
            default:
                throw new IllegalArgumentException("不支持的浏览器类型");
        }
    }

    public WebDriver driver() {
        return this.driver("");
    }

    /**
     * 获取各浏览器的驱动
     * @param downPrefix 下载路径前缀,如果由文件下载需求，防止获取到网页的下载文件
     * @author: qiang.zhou
     * @date: 2025/2/28 16:12
     * @return org.openqa.selenium.WebDriver
     **/
    public WebDriver driver(String downPrefix) {
        switch (type) {
            case CHROME:
                ChromeOptions chromeOptions = new ChromeOptions();
                if (StringUtils.isNotBlank(downloadPath)) {
                    HashMap<String, Object> chromePrefs = new HashMap<>();
                    chromePrefs.put("download.default_directory", downloadPath + downPrefix);
                    // 禁用弹窗
                    chromePrefs.put("download.prompt_for_download", false);
                    chromePrefs.put("download.directory_upgrade", true);
                    chromeOptions.setExperimentalOption("prefs", chromePrefs);
                }

                // 无界面
                chromeOptions.addArguments("--headless=new");
                // 允许下载
                chromeOptions.addArguments("--allow-downloads");
                // 远程允许所有来源
                chromeOptions.addArguments("--remote-allow-origins=*");
                // 忽略SSL错误
                chromeOptions.addArguments("--ignore-ssl-errors=yes");
                // 忽略证书错误
                chromeOptions.addArguments("--ignore-certificate-errors");
                if (StringUtils.isNotBlank(binaryPath)) {
                    chromeOptions.setBinary(binaryPath);
                }
                return new ChromeDriver(chromeOptions);
            case FIREFOX:
                FirefoxOptions firefoxOptions = new FirefoxOptions();
                if (StringUtils.isNotBlank(downloadPath)) {
                    FirefoxProfile profile = new FirefoxProfile();
                    profile.setPreference("browser.download.dir", downloadPath + downPrefix);
                    // 2表示自定义路径
                    profile.setPreference("browser.download.folderList", 2);

                    firefoxOptions.setCapability("moz:firefoxOptions", ImmutableMap.of("log", ImmutableMap.of("level", "SEVERE")));

                    firefoxOptions.setProfile(profile);
                }

                // 无界面
                firefoxOptions.setHeadless(true);
                firefoxOptions.addArguments("--allow-downloads");
                if (StringUtils.isNotBlank(binaryPath)) {
                    firefoxOptions.setBinary(binaryPath);
                }
//                firefoxOptions.setHeadless(false); // 是否以无头模式启动，false 为显示浏览器界面


                return new FirefoxDriver(firefoxOptions);
            case EDGE:
                EdgeOptions edgeOptions = new EdgeOptions();
                if (StringUtils.isNotBlank(downloadPath)) {
                    HashMap<String, Object> edgeMap = new HashMap<>();
                    edgeMap.put("download.default_directory", downloadPath + downPrefix);
                    // 禁用弹窗
                    edgeMap.put("download.prompt_for_download", false);
                    edgeMap.put("download.directory_upgrade", true);
                    edgeOptions.setExperimentalOption("prefs", edgeMap);
                }

                // 无界面
                edgeOptions.addArguments("--headless");
                edgeOptions.addArguments("--allow-downloads");
                // 远程允许所有来源
                edgeOptions.addArguments("--remote-allow-origins=*");
                // 忽略SSL错误
                edgeOptions.addArguments("--ignore-ssl-errors=yes");
                // 忽略证书错误
                edgeOptions.addArguments("--ignore-certificate-errors");
                if (StringUtils.isNotBlank(binaryPath)) {
                    edgeOptions.setBinary(binaryPath);
                }
                return new EdgeDriver(edgeOptions);
            default:
                throw new IllegalArgumentException("不支持的浏览器类型");
        }
    }
    
    /**
     * 这里扫描的浏览器下载地址中的所有文件
     * 所以为保证准确性，最好保证文件夹内同时只存在一个文件
     * @author: qiang.zhou
     * @date: 2025/2/28 16:26
     * @param timeoutInSeconds  
     * @return boolean       
     **/
    @SneakyThrows
    public File waitForDownloadToComplete(String downPrefix, int timeoutInSeconds) {
        Thread.sleep(1000);
        long endTime = System.currentTimeMillis() + timeoutInSeconds * 1000L;
        while (System.currentTimeMillis() < endTime) {
            File[] files = new File(downloadPath + downPrefix).listFiles();
            if (files != null) {
                List<File> fileList = Stream.of(files)
                        .sorted(Comparator.comparing(file -> {
                            try {
                                // 获取文件的创建时间
                                BasicFileAttributes attrs = Files.readAttributes(((File)file).toPath(), BasicFileAttributes.class);
                                // 转换为毫秒进行比较
                                return attrs.creationTime().toMillis();
                            } catch (Exception e) {
                                // 如果出错，返回默认值
                                return 0L;
                            }
                        }).reversed()).collect(Collectors.toList());
                for (File file : fileList) {
                    if (file.getName().endsWith(".crdownload") || file.getName().endsWith(".tmp") || file.getName().endsWith(".part")) {
                        break;
                    } else {
                        if (ObjectUtils.isNotEmpty(fileList)) {
                            return fileList.get(0);

                        }
                    }
                }
            }
            Thread.sleep(1000);
        }
        return null;
    }

    /**
     * 校验采购标题是否存在设置的关键字
     * @author: qiang.zhou
     * @date: 2025/4/11 15:41
     * @param title
     * @return boolean
     **/
    public boolean checkFilterTitle(String title) {
        return ObjectUtils.isNotEmpty(filterTitles) && filterTitles.stream().anyMatch(title::contains);
    }


}
